#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

template <class T>
class BlockQueue
{
private:
    std::queue<T> _bq;      //阻塞队列
    int _capacity;          //容量上限
    pthread_mutex_t _mtx;   //通过互斥锁保证队列的安全
    pthread_cond_t _Empty;//表示bq是否为空
    pthread_cond_t _Full; //表示bq是否为满

public:
    BlockQueue(int capacity = 5 ):_capacity(capacity)
    {
        //初始化 互斥量 和 条件变量
        pthread_mutex_init(&_mtx,nullptr);
        pthread_cond_init(&_Empty,nullptr);
        pthread_cond_init(&_Full,nullptr);
    }

    ~BlockQueue()
    {
        //释放 互斥量 和 条件变量
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_Empty);
        pthread_cond_destroy(&_Full);
    }

     bool isQueueEmpty()
    {
        return _bq.size() == 0;
    }

    bool isQueueFull()
    {
        return _bq.size() == _capacity;
    }

    void push(const T& x)
    {
        pthread_mutex_lock(&_mtx);
        //1.先检测当前的临界资源是否能够满足访问条件
        // pthread_cond_wait第二个参数是一个锁，当成功调用wait之后，传入的锁，会被自动释放！
        // 当我们被唤醒的时候，pthread_cond_wait，会自动帮助我们线程获取锁
        // pthread_cond_wait: 但是只要是一个函数，就可能调用失败
        // pthread_cond_wait: 可能存在 伪唤醒 的情况
        while(isQueueFull()) pthread_cond_wait(&_Full,&_mtx);
        //2.访问临界资源,100%确定,资源是就绪的
        _bq.push(x);
        pthread_cond_signal(&_Empty);
        pthread_mutex_unlock(&_mtx);
    }

    void pop(T& x)
    {
        pthread_mutex_lock(&_mtx);
        while(isQueueEmpty()) pthread_cond_wait(&_Empty,&_mtx);

        x = _bq.front();
        _bq.pop();
        pthread_cond_signal(&_Full);
        pthread_mutex_unlock(&_mtx);
    }

};